<?php
/**
 * Author:xsc
 * Time:2023/9/1 10:58
 */

namespace App\Service\Common;

use App\Jobs\MerchansCastingJob;
use App\Params\Common\SpecialDeleteParams;
use App\Params\Common\SpecialEditParams;
use App\Params\Common\SpecialEditStatusParams;
use App\Repository\ImageRepo;
use App\Service\BaseService;
use App\Common\ResponseCode;
use App\Exceptions\BusinessException;
use App\Params\Common\CouponParams;
use App\Params\Common\CouponStockParam;
use App\Params\Common\SpecialCreateParams;
use App\Params\Common\SpecialDetailParams;
use App\Params\Common\SpecialPrivilegeParams;
use App\Params\Common\SpecialSendParams;
use App\Extensions\Coupon\CouponService;
use App\Repository\SpecialAlbumRepo;
use App\Repository\ConfigRepo;
use App\Repository\MerchantsInfoRepo;
use App\Repository\SpecialRepo;
use App\Repository\SpecialPrivilegeRepo;
use App\Repository\GoodsRepo;
use App\Repository\MerchantsCastingRepo;
use App\Traits\MoneyCalculator;
use Illuminate\Support\Carbon;
use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Facades\DB;
use App\Repository\MemberRepo;
use App\Extensions\Common\OssService;
use App\Repository\SpecialOpenOrderRepo;
use App\Common\Tools;
use App\Extensions\Common\RedisService;
use Illuminate\Support\Facades\Log;
use App\Common\Constant;
use Illuminate\Support\Facades\Redis;
use App\Extensions\Common\ChainService;
use App\Repository\GoodsLogRepo;
use App\Jobs\PrivilegeAutoReceiveJob;


class SpecialService extends BaseService
{
    use MoneyCalculator;

    /**
     * 卡券列表
     * @param CouponParams $params
     * @return array
     * @throws BusinessException
     * Author:xsc
     * Time:2023/9/4 18:55
     */
    public function couponList(CouponParams $params)
    {
        $access_mid = $params->getMid();
        $sku_name = $params->getSkuName() ?: '';// 货品名称, 支持模糊查询,可选
        $filter_red_envelope = $params->getFilterRedEnvelope() ?: 0;// 是否过滤红包 0不过滤 1过滤 默认0
        $page = $params->getPage() ?: 0;
        $page_size = $params->getPageSize() ?: 0;

        // 接入商户信息
        $merchants_info = MerchantsInfoRepo::getInstance()->getInfoByAccessMid($access_mid);
        if (!$merchants_info) {
            throw new BusinessException(ResponseCode::MERCHANTS_NOT_FUND);
        }

        // 卡券
        $coupon = CouponService::getInstance();
        $res = $coupon->listChannelSku($access_mid, $sku_name, $filter_red_envelope, $page, $page_size);

        $total = $res['total'];
        $coupon_list = $res['rows'];

        $list = [];
        foreach ($coupon_list as $value) {
            $list[] = [
                'sku_code' => $value['skuCode'],
                'sku_name' => $value ['skuName'],
                'spu_type' => $value ['spuType'],
                'code_end_type' => $value['codeEndType'],
                'code_end_date' => $value['codeEndDate'],
                'code_end_days' => $value['codeEndDays'],
                'sale_amount' => $value['saleAmount'],
                'pic_url' => isset($value['skuDetailPics'][0]['picUrl']) ? $value['skuDetailPics'][0]['picUrl'] : '',
            ];
        }

        $data = [
            'total' => $total,
            'list' => $list,
        ];

        return $data;
    }

    /**
     * 卡券库存
     * @param CouponStockParam $params
     * @return array
     * @throws BusinessException
     * Author:xsc
     * Time:2023/9/14 17:08
     */
    public function couponStock(CouponStockParam $params)
    {
        $access_mid = $params->getMid();
        $sku_code = $params->getSkuCode();

        // 接入商户信息
        $merchants_info = MerchantsInfoRepo::getInstance()->getInfoByAccessMid($access_mid);
        if (!$merchants_info) {
            throw new BusinessException(ResponseCode::MERCHANTS_NOT_FUND);
        }

        // 卡券
        $coupon = CouponService::getInstance();

        $stock = $coupon->getStock($access_mid, $sku_code);

        $data['stock'] = $stock;
        return $data;
    }

    /**
     * 查询创作者专辑,不存在就创建一个默认专辑
     * @param array $merchants_info
     * @return array|int[]
     * @throws BusinessException
     * Author:xsc
     * Time:2023/9/6 16:06
     */
    private function checkAlbum(array $merchants_info)
    {
        $merchants_id = $merchants_info['merchants_id'];

        $album = SpecialAlbumRepo::getInstance()->getAlbum($merchants_id);
        if (!$album) {
            // 不存在创建一个默认专辑
            $data = [
                'merchants_id' => $merchants_id,// 商户id
                'name' => $merchants_info['user_name'],// 专辑名称->创作者简称
                'desc' => $merchants_info['user_name'],// 专辑描述->创作者简称
                'cover' => ConfigRepo::getInstance()->getConfigValue('default_album_cover'),// 默认专辑封面
                'sell_count' => 0,
                'sell_money_count' => 0,
                'is_resale' => 1,// 可转售
                'is_donation' => 1,// 可转赠
            ];
            $album_id = SpecialAlbumRepo::getInstance()->save($data);
            if (!$album_id) {
                Log::info('创建默认专辑失败:' . $merchants_id);
                throw new BusinessException(ResponseCode::SPECIAL_ALBUM_CREATE_FAIL);
            }
            $album = array_merge(['id' => $album_id], $data);
        }
        return $album;
    }

    /**
     * @throws BusinessException
     */
    public function edit(SpecialEditParams $params): array
    {
        // 接入商户信息
        $merchants_info = MerchantsInfoRepo::getInstance()->getInfoByAccessMid($params->getMid());
        if (!$merchants_info) {
            throw new BusinessException(ResponseCode::MERCHANTS_NOT_FUND);
        }

        $album = SpecialAlbumRepo::getInstance()->getAlbumById($params->getAlbumId());
        if (empty($album) || $album->merchants_id != $merchants_info['merchants_id']) {
            throw new BusinessException(ResponseCode::SPECIAL_ALBUM_NOT_FOUND);
        }

        // 创作者的藏品详情
        $special_info = SpecialRepo::getInstance()->getById($params->getSpecialId());

        if (empty($special_info) || $special_info->merchants_id != $merchants_info['merchants_id']) {
            throw new BusinessException(ResponseCode::SPECIAL_NOT_FUND);
        }

        // 已审核状态下 需先下架才可编辑
        if ($special_info['apply_status'] == 4) {
            throw new BusinessException(ResponseCode::PARAM_ILLEGAL, ['error' => '已撤销藏品不可编辑']);
        }

        // 已审核状态下 需先下架才可编辑
        if ($special_info['apply_status'] == 2 && $special_info['status'] == 1) {
            throw new BusinessException(ResponseCode::PARAM_ILLEGAL, ['error' => '请先下架藏品后进行编辑']);
        }

        $edit_data = [
            'label' => '"' . implode(",", str_replace(",", " ", $params->getLabel())) . '"',// 标签
            'base_price' => $this->format($params->getPrice()),// 价格
            'special_album_id' => $album['id'], // 专辑
            'is_resale' => $album['is_resale'],
            'is_donation' => $album['is_donation'],
            'sell_time' => $params->getSellTime(),// 售卖时间
            "user_num" => $params->getUserNum() ?: $special_info['user_num'],
            "one_num" => $params->getOneNum() ?: $special_info['one_num'],
            'story' => $params->getStory(),// 作品故事
            "update_time" => Carbon::now(),
            "apply_status" => 1,
        ];

        // 已审核且已下架状态更新审核状态为待审核
//        if ($special_info['apply_status'] == 2 && $special_info['status'] == 0)
//            $edit_data['apply_status'] = 1;

        // 更新藏品信息
        $res = SpecialRepo::getInstance()->save($edit_data, $params->getSpecialId());
        if (!$res) {
            Log::info('更新藏品信息失败:' . $params->getSpecialId());
            throw new BusinessException(ResponseCode::SPECIAL_UPDATE_FAIL);
        }

        return [
            'special_id' => $params->getSpecialId(),
        ];
    }

    /**
     * 新增/更新藏品
     * @param SpecialCreateParams $params
     * @return array
     * @throws BusinessException
     * @throws \OSS\Core\OssException
     * Author:xsc
     * Time:2023/10/16 16:28
     */
    public function create(SpecialCreateParams $params): array
    {
        $access_mid = $params->getMid();
        $title = $params->getTitle();
        $all_count = $params->getAllCount();
        $thumb = $params->getThumb();
        $story = $params->getStory();

        $lock = Cache::lock("special_create_" . $access_mid, 30);
        if (!$lock->get()) {
            throw new BusinessException(ResponseCode::REPEAT_AGAIN);
        }
        // 接入商户信息
        $merchants_info = MerchantsInfoRepo::getInstance()->getInfoByAccessMid($access_mid);
        if (!$merchants_info) {
            $lock->release();
            throw new BusinessException(ResponseCode::MERCHANTS_NOT_FUND);
        }

        // 商户id
        $merchants_id = $merchants_info['merchants_id'];

        // 新增时判断剩余铸造数不足
        if ($merchants_info['remaining'] < $all_count) {
            $lock->release();
            throw new BusinessException(ResponseCode::MERCHANTS_REMAIN_CAST_NUM_NOT_ENOUGH);
        }

        // 查询专辑
        $album = SpecialAlbumRepo::getInstance()->getAlbumById($params->getAlbumId());
        if (empty($album) || $album->merchants_id != $merchants_id) {
            $lock->release();
            throw new BusinessException(ResponseCode::SPECIAL_ALBUM_NOT_FOUND);
        }


        if (empty($params->getThumb()) && empty($params->getThumbId())) {
            $lock->release();
            throw new BusinessException(ResponseCode::PARAM_ILLEGAL);
        }
        if ($params->getThumbId()) {
            $image = ImageRepo::getInstance()->getById($params->getThumbId());
            if (empty($image) || $image->is_used != 0 || $image->is_delete != 0) {
                $lock->release();
                throw new BusinessException(ResponseCode::SPECIAL_IMAGE_NOT_FOUND);
            }
            $thumb = $image->url;
        } elseif ($params->getThumb()) {
            // 转存oss
            $thumb = OssService::getInstance()->saveFile($thumb);
        }


        if ($params->getType() == 1) {
            if ($this->compare($params->getPrice(), "0.50") < 0) {
                $lock->release();
                throw new BusinessException(ResponseCode::SPECIAL_PRICE_ILLEGAL);
            }
        }
        $insert_data = [
            'title' => $title,// 专题名称
            'thumb' => $thumb,// 专题展示图
            'story' => $story,// 作品故事
            'resources_type' => $params->getResourceType(),// 藏品图片类型
            'merchants_id' => $merchants_id,// 商户id
            'base_price' => $this->format($params->getPrice()),// 价格
            'special_type_id' => 1,// 专题分类ID
            'all_count' => $all_count,// 数量
            'status' => 0,// 上下架,0下架
            'is_sell_out' => 0,// 是否售罄 1是0否
            'cast_price' => 0,// 铸造服务费
            'sort' => 0,// 排序号
            'apply_status' => 1,// 审核状态 1审核中
            'reason' => '',// 拒绝原因
            'cast_status' => 1,// 铸造状态 1未铸造
            'sell_status' => 1,
            'sell_time' => $params->getSellTime(),// 售卖时间
            'special_album_id' => $album['id'],
            'is_resale' => $album['is_resale'],
            'is_donation' => $album['is_donation'],
            'type' => $params->getType(),// 类型
            'label' => '"' . implode(",", str_replace(",", " ", $params->getLabel())) . '"',
            "user_num" => $params->getUserNum() ?: $params->getAllCount(),
            "one_num" => $params->getOneNum() ?: 500,
        ];

        try {
            DB::beginTransaction();

            // 1.新增藏品
            $insert_data['create_time'] = date("Y-m-d H:i:s", time());
            $special_id = SpecialRepo::getInstance()->save($insert_data);
            if (!$special_id) {
                throw new \Exception("新增藏品失败");
            }

            // 2.修改创作者剩余可铸造量
            $cast_update = MerchantsCastingRepo::getInstance()->updateRemaining($merchants_id, $all_count);
            if (!$cast_update) {
                throw new \Exception("修改创作者剩余可铸造量失败");
            }

            if ($params->getThumbId()) {
                $ret = ImageRepo::getInstance()->setUsed($params->getThumbId(), ['access_mid' => $params->getMid(), 'special_id' => $special_id, 'used_time' => Carbon::now()]);
                if (!$ret) {
                    throw new \Exception("修改创作者图片信息失败");
                }
            }

            DB::commit();
            Redis::hset(Constant::SPECIAL_STOCK_REDIS_KEY, (string)$special_id, (int)$params->getAllCount());

        } catch (\Exception $e) {
            DB::rollBack();
            Log::info("新增藏品异常:{$merchants_id}," . $e->getMessage());
            $lock->release();
            throw new BusinessException(ResponseCode::SPECIAL_CREATE_ERROR);
        }

        // 写入创作者铸造消耗日志
        $serial_number = MerchantsCastingService::getInstance()->checkSerialNumber(Tools::build_hash_no());
        $log = [
            'mer_id' => $merchants_id,
            'access_id' => $access_mid,
            'num' => $all_count,
            'serial_number' => $serial_number,
            'before_change' => $merchants_info['remaining'],
            'after_change' => $merchants_info['remaining'] - $all_count,
            'source' => 2,
            'type' => 3,
            'create_time' => date('Y-m-d H:i:s',),
            'update_time' => date('Y-m-d H:i:s',),
        ];
        // 异步写入藏品铸造数量消耗日志
        dispatch(new MerchansCastingJob($log, $special_id));
        $lock->release();

        return [
            'special_id' => $special_id,
        ];
    }

    /**
     * 藏品详情
     * @param SpecialDetailParams $params
     * @return array
     * @throws BusinessException
     * Author:xsc
     * Time:2023/9/5 10:15
     */
    public function detail(SpecialDetailParams $params)
    {
        $access_mid = $params->getMid() ?: '';
        $special_id = $params->getSpecialId();

        if ($access_mid) {
            // 接入商户信息
            $merchants_info = MerchantsInfoRepo::getInstance()->getInfoByAccessMid($access_mid);
            if (!$merchants_info) {
                throw new BusinessException(ResponseCode::MERCHANTS_NOT_FUND);
            }
        }

        // 创作者的藏品详情
        $special_info = SpecialRepo::getInstance()->detail($special_id, $access_mid);
        if (!$special_info) {
            throw new BusinessException(ResponseCode::SPECIAL_NOT_FUND);
        }

        $data = [
            'special_id' => $special_info['special_id'],
            'title' => $special_info['title'],
            'thumb' => $special_info['thumb'],
            'all_count' => $special_info['all_count'],
            'create_time' => $special_info['create_time'],
            'apply_status' => $special_info['apply_status'],
            'reason' => $special_info['reason'],
            'cast_status' => $special_info['cast_status'],
            'cast_count' => $special_info['cast_count'],
            'status' => $special_info['status'],
        ];

        return $data;
    }

    /**
     * 藏品赋能
     * @param SpecialPrivilegeParams $params
     * @return array
     * @throws BusinessException
     * @throws \RedisException
     * Author:xsc
     * Time:2023/9/11 19:20
     */
    public function privilege(SpecialPrivilegeParams $params)
    {
        $access_mid = $params->getMid();
        $special_id = $params->getSpecialId();
        $sku_code = $params->getSkuCode();

        // 如果是创建红包,有下面几个参数
        $sku_name = $params->getSkuName();
        $type = $params->getType();
        $stock = $params->getStock();
        $end_date = $params->getEndDate();
        $amount = $params->getAmount();

        // 频率限制
        $redis = RedisService::getInstance();
        $access_key = Constant::SPECIAL_PRIVILEGE_ACCESS . $access_mid . '_' . $special_id . '_' . $sku_code;
        $redis->accessControl($access_key, 5);

        // 接入商户信息
        $merchants_info = MerchantsInfoRepo::getInstance()->getInfoByAccessMid($access_mid);
        if (!$merchants_info) {
            throw new BusinessException(ResponseCode::MERCHANTS_NOT_FUND);
        }

        // 商户id
        $merchants_id = $merchants_info['merchants_id'];

        // 创作者的藏品详情
        $special_info = SpecialRepo::getInstance()->detail($special_id, $access_mid);
        if (!$special_info) {
            throw new BusinessException(ResponseCode::SPECIAL_NOT_FUND);
        }

        // 藏品未通过审核
        if ($special_info['apply_status'] != 2) {
            throw new BusinessException(ResponseCode::SPECIAL_APPLY_FAIL);
        }

        // 藏品未铸造完成
        if ($special_info['cast_status'] != 3) {
            throw new BusinessException(ResponseCode::SPECIAL_CAST_NOT_FINISH);
        }

        // 赋能总量
        $goods_count = GoodsRepo::getInstance()->getCountBySpecialId($special_id);

        // 卡券
        $coupon = CouponService::getInstance();

        // 是否是创建红包
        if ($sku_code && $sku_name && $type && $stock && $end_date && $amount) {
            // 创建红包

            // 限制红包数量和藏品铸造的数量必须相等
            if ($stock != $goods_count) {
                throw new BusinessException(ResponseCode::ENVELOPE_STOCK_CAST_COUNT_NOT_EQUAL);
            }

            // 限制红包有效期
            if (strtotime($end_date) < strtotime(date('Ymd'))) {
                throw new BusinessException(ResponseCode::ENVELOPE_END_DATE_ERROR);
            }

            // 限制红包金额>0.01
            if ($amount < 0.01) {
                throw new BusinessException(ResponseCode::ENVELOPE_AMOUNT_ERROR);
            }

            $envelope = $coupon->createRedEnvelope($access_mid, $sku_code, $sku_name, $type, $stock, $end_date, $amount);
            if (!$envelope) {
                Log::info('创建红包失败:' . $access_mid);
                throw new BusinessException(ResponseCode::ENVELOPE_CREATE_FAIL);
            }

            // 读取配置中的红包信息配置
            $config_info = ConfigRepo::getInstance()->getConfigValue('envelope_' . $type);
            $config_info = json_decode($config_info, true);

            // 组装赋能信息
            $title = str_replace('{$amount}', $amount, $config_info['title']);
            $desc = $config_info['desc'];
            $content = $config_info['content'];
            $notice = $config_info['notice'];
            $thumb = $config_info['thumb'];
            $coupon_id = '';
            $coupon_validity_time = date('Y-m-d 23:59:59', strtotime($end_date));
            $envelope_type = $type;
            $envelope_amount = $amount;
            $privilege_type = 4;// 红包
        } else {
            // 卡券
            $coupon_info = $coupon->getChannelSku($access_mid, $sku_code);
            if (!$coupon_info) {
                throw new BusinessException(ResponseCode::COUPON_NOT_FUND);
            }

            // 卡券过期
            if ($coupon_info['codeEndType'] == 1 && strtotime($coupon_info['codeEndDate']) < strtotime(date('Ymd'))) {
                throw new BusinessException(ResponseCode::COUPON_EXPIRED);
            }

            // 卡券库存
            $coupon_stock = $coupon->getStock($access_mid, $sku_code);
            if (!$coupon_stock || $coupon_stock <= 0) {
                throw new BusinessException(ResponseCode::COUPON_OUT_OF_STOCK);
            }

            // 库存不足
            if ($coupon_stock < $goods_count) {
                throw new BusinessException(ResponseCode::COUPON_OUT_OF_STOCK);
            }

            // 组装赋能信息
            $title = $coupon_info['skuName'];
            $desc = $coupon_info['spuName'];
            $content = $coupon_info['skuDetail'] ?: '';
            $notice = $coupon_info['spuDetail'] ?: '';

            $thumb = '';
            if (isset($coupon_info['skuDetailPics'][0]['picUrl'])) {
                $thumb = OssService::getInstance()->saveFile($coupon_info['skuDetailPics'][0]['picUrl']);
            }

            $coupon_id = $coupon_info['spuCode'];
            if ($coupon_info['codeEndType'] == 1) {
                $coupon_validity_time = date('Y-m-d 23:59:59', strtotime($coupon_info['codeEndDate']));
            }
            $envelope_type = '';
            $envelope_amount = '';
            $privilege_type = 0;// 卡券
        }

        $insert_data = [
            'type' => $privilege_type,// 0卡券4红包
            'title' => $title,// 赋能名称
            'thumb' => $thumb,// 展示图
            'desc' => $desc,// 简述
            'content' => $content,// 详情
            'notice' => $notice,// 须知
            'merchants_id' => $merchants_id,// 商户ID
            'special_id' => $special_id,// 藏品ID
            'coupon_code' => $sku_code,// 卡券编号
            'coupon_id' => $coupon_id,
            'coupon_validity_time' => $coupon_validity_time,
            'send_type' => 0,// 发放类型
            'status' => 1,//上架
            'num' => $goods_count,
            'create_time' => date('Y-m-d H:i:s', time()),
            'envelope_type' => $envelope_type,
            'envelope_amount' => $envelope_amount,
            'channel_code' => $access_mid,
        ];

        $privilege_id = SpecialPrivilegeRepo::getInstance()->save($insert_data);
        if (!$privilege_id) {
            throw new BusinessException(ResponseCode::COUPON_PRIVILEGE_FAIL);
        }

        // 查询当前藏品的拥有人(追加赋能,自动领取)
        $goods_list = GoodsRepo::getInstance()->getListBySpecialId($special_id);

        if ($goods_list) {
            foreach ($goods_list as $value) {
                dispatch(new PrivilegeAutoReceiveJob($privilege_id, $value['id']));
            }
        }

        $data = [
            'privilege_id' => $privilege_id,
        ];

        $redis->del($access_key);
        return $data;
    }

    /**
     * 发放藏品
     * @param SpecialSendParams $params
     * @return array
     * @throws BusinessException
     * @throws \RedisException
     * Author:xsc
     * Time:2023/9/7 9:24
     */
    public function send(SpecialSendParams $params)
    {
        $access_mid = $params->getMid();
        $special_id = $params->getSpecialId();
        $mobile = $params->getMobile();
        $out_order_no = $params->getOutOrderNo();

        // 频率限制
        $redis = RedisService::getInstance();
        $access_key = Constant::SPECIAL_SEND_ACCESS . $out_order_no;
        $redis->accessControl($access_key, 5);

        // 接入商户信息
        $merchants_info = MerchantsInfoRepo::getInstance()->getInfoByAccessMid($access_mid);
        if (!$merchants_info) {
            throw new BusinessException(ResponseCode::MERCHANTS_NOT_FUND);
        }

        // 商户id
        $merchants_id = $merchants_info['merchants_id'];

        // 查询专辑
        $album = $this->checkAlbum($merchants_info);

        // 创作者的藏品详情
        $special_info = SpecialRepo::getInstance()->detail($special_id, $access_mid);
        if (!$special_info) {
            throw new BusinessException(ResponseCode::SPECIAL_NOT_FUND);
        }

        // 藏品未上架
        if ($special_info['status'] != 1) {
            throw new BusinessException(ResponseCode::SPECIAL_NOT_UP);
        }

        // 藏品未通过审核
        if ($special_info['apply_status'] != 2) {
            throw new BusinessException(ResponseCode::SPECIAL_APPLY_FAIL);
        }

        // 藏品未铸造完成
        if ($special_info['cast_status'] != 3) {
            throw new BusinessException(ResponseCode::SPECIAL_CAST_NOT_FINISH);
        }

        // 用户信息
        $member = MemberRepo::getInstance()->getInfoByMobile($mobile);

        if (!$member) {
            throw new BusinessException(ResponseCode::USERNOTFUND);
        }

        // 用户id
        $member_id = $member['member_id'];

        // 未实名
        if ($member['real_name_status'] != 2) {
            throw new BusinessException(ResponseCode::USER_REAL_NAME_NOT_FINISHED);
        }

        // 订单已存在
        $order = SpecialOpenOrderRepo::getInstance()->getByOutOrderNo($out_order_no);
        if ($order) {
            throw new BusinessException(ResponseCode::ORDER_EXISTS);
        }

        //$goods_id = 3489;//test
        //并发场景下,redis取出的商品id可能已被占用,此处尝试最多三次
        for ($i = 0; $i < 3; $i++) {
            $goods_id = $this->getGoodsId($special_id);
            if (!$goods_id) {
                throw new BusinessException(ResponseCode::GOODS_OUT_OF_STOCK);
            }

            $goods_info = GoodsRepo::getInstance()->getUnusedGoods($goods_id);
            if ($goods_info) {
                break;
            }
        }
        if (!$goods_info) {
            Log::info('获取goods_id异常:' . $special_id);
            throw new BusinessException(ResponseCode::GOODS_OUT_OF_STOCK);
        }

        // 初始化订单
        $order_no = Tools::build_hash_no();
        $insert_data = [
            'order_no' => $order_no,
            'out_order_no' => $out_order_no,
            'merchants_id' => $merchants_id,
            'special_id' => $special_id,
            'goods_id' => $goods_id,
            'member_id' => $member_id,
            'status' => 0,
            'create_time' => date("Y-m-d H:i:s", time()),
            'update_time' => date("Y-m-d H:i:s", time()),
        ];
        $order_id = SpecialOpenOrderRepo::getInstance()->save($insert_data);

        if (!$order_id) {
            throw new BusinessException(ResponseCode::ORDER_CREATED_FAIL);
        }

        // 处理订单
        try {
            DB::beginTransaction();

            // 1.更新商品信息
            $data1 = [
                'status' => 3,
                'member_id' => $member_id,
                'price' => $special_info['base_price'],
                'stand_time' => date("Y-m-d H:i:s", time()),
                'update_time' => date("Y-m-d H:i:s", time()),
            ];
            $res1 = GoodsRepo::getInstance()->save($data1, $goods_id);

            // 2.更新商品销量
            $res2 = GoodsRepo::getInstance()->updateSaleCount($goods_id, 1);

            // 3.更新藏品销量
            $res3 = SpecialRepo::getInstance()->updateSaleCount($special_id, 1);

            // 4.更新专辑销量
            $res4 = SpecialAlbumRepo::getInstance()->updateSellCount($album['id'], 1);

            // 5.增加商品交易记录
            $data5 = [
                'goods_id' => $goods_id,
                'special_id' => $special_id,
                'from_member_id' => 0,
                'to_member_id' => $member_id,
                'order_sn' => Tools::build_hash_no(),
                'content' => "开放平台发放",
                'type' => 2,
                'source' => 3,
                'create_time' => date('Y-m-d H:i:s')
            ];
            $res5 = GoodsLogRepo::getInstance()->save($data5);

            if (!$res1 || !$res2 || !$res3 || !$res4 || !$res5) {
                Log::info('处理订单异常' . $order_id . '|' . $res1 . '|' . $res2 . '|' . $res3 . '|' . $res4 . '|' . $res5);
                throw new BusinessException(ResponseCode::ORDER_DEAL_ERROR);
            }

            // 6.nft转账
            $chain = ChainService::getInstance();
            $res6 = $chain->transferFrom($merchants_info['ca_address'], $member['ca_account_address'], $goods_info['chain_mark']);
            if (!$res6) {
                Log::info('处理订单异常,nft转账失败');
                throw new BusinessException(ResponseCode::ORDER_DEAL_ERROR);
            }

            // 7.更新goods_log的hash
            $res7 = GoodsLogRepo::getInstance()->save(['hash' => $res6], $res5);

            // 8.订单状态为成功
            $res8 = SpecialOpenOrderRepo::getInstance()->save(['status' => 1, 'update_time' => date("Y-m-d H:i:s", time()),], $order_id);

            DB::commit();
        } catch (\Exception $e) {
            DB::rollBack();

            // 更新订单状态为失败
            SpecialOpenOrderRepo::getInstance()->save(['status' => 2, 'update_time' => date("Y-m-d H:i:s", time()),], $order_id);

            // goods_id放回到redis队列最后
            $this->pushGoodsId($special_id, $goods_id);

            Log::info('处理订单失败' . $order_id . $e->getMessage());
            throw new BusinessException(ResponseCode::ORDER_DEAL_FAIL);
        }

        // 处理自动领取
        $this->dealPrivilegeAutoReceive($special_id, $goods_id);

        $data = [
            'order_no' => $order_no,
            'out_order_no' => $out_order_no,
            'special_id' => $special_id,
            'goods_id' => $goods_id,
            'code' => $goods_info['code'],
        ];

        $redis->del($access_key);
        return $data;
    }

    /**
     *  编辑special上下架状态
     * @param SpecialEditStatusParams $param
     * @return int
     * @throws BusinessException
     */
    public function editSpecialStatus(SpecialEditStatusParams $param): int
    {
        // 接入商户信息
        $merchants_info = MerchantsInfoRepo::getInstance()->getInfoByAccessMid($param->getMid());
        if (!$merchants_info) {
            throw new BusinessException(ResponseCode::MERCHANTS_NOT_FUND);
        }

        // 创作者的藏品详情
        $specialInfo = SpecialRepo::getInstance()->getById($param->getSpecialId());

        if (empty($specialInfo) || $specialInfo->merchants_id != $merchants_info['merchants_id']) {
            throw new BusinessException(ResponseCode::SPECIAL_NOT_FUND);
        }

        if ($specialInfo->is_join_privilege_activity == 1){
            throw new BusinessException(ResponseCode::SPECIAL_JOIN_PRIVILEGE_ACTIVITY_ERR);
        }

        if ($specialInfo->apply_status != 2) {
            throw new BusinessException(ResponseCode::SPECIAL_CANT_CHANGE_STATUS);
        }

        if ($specialInfo->cast_status != 3) {
            throw new BusinessException(ResponseCode::SPECIAL_CANT_STATUS_ERR);
        }
        $edit_data = [
            "status" => $param->getStatus(),
            "update_time" => Carbon::now(),
        ];
        // 更新藏品信息
        return SpecialRepo::getInstance()->save($edit_data, $param->getSpecialId());
    }

    /**
     *  删除-还原铸造数
     * @param SpecialDeleteParams $param
     * @return int
     * @throws BusinessException
     */
    public function deleteSpecial(SpecialDeleteParams $param): int
    {
        // 接入商户信息
        $merchants_info = MerchantsInfoRepo::getInstance()->getInfoByAccessMid($param->getMid());
        if (!$merchants_info) {
            throw new BusinessException(ResponseCode::MERCHANTS_NOT_FUND);
        }

        // 创作者的藏品详情
        $specialInfo = SpecialRepo::getInstance()->getById($param->getSpecialId());

        if (empty($specialInfo) || $specialInfo->merchants_id != $merchants_info['merchants_id']) {
            throw new BusinessException(ResponseCode::SPECIAL_NOT_FUND);
        }

        if ($specialInfo->is_join_privilege_activity == 1){
            throw new BusinessException(ResponseCode::SPECIAL_JOIN_PRIVILEGE_ACTIVITY_ERR);
        }

        if ($specialInfo->apply_status != 3) {
            throw new BusinessException(ResponseCode::SPECIAL_CANT_DELETE);
        }

        $edit_data = [
            "apply_status" => 4,
            "update_time" => Carbon::now(),
        ];
        // 更新藏品信息
        $saveFlag = SpecialRepo::getInstance()->save($edit_data, $param->getSpecialId());

        // 更新创作者创作总数
        MerchantsCastingRepo::getInstance()->updateTotal($specialInfo->merchants_id, $specialInfo['all_count'], 'add');
        MerchantsCastingRepo::getInstance()->updateIncRemaining($specialInfo->merchants_id, $specialInfo['all_count']);

        // 更新当前藏品图片为未使用
        $save = ['is_used'=>0,'used_time'=>null,'access_mid'=>'','update_time'=>date('Y-m-d H:i:s')];
        ImageRepo::getInstance()->saveBySpecialId($save,$param->getSpecialId());

        // 更新成功后异步执行铸造流水日志
        if ($saveFlag) {
            // 指定撤销流水号
            $revoke_number = $specialInfo['serial_number'];

            // 写入创作者铸造消耗日志
            $serial_number = MerchantsCastingService::getInstance()->checkSerialNumber(Tools::build_hash_no());

            $log = [
                'mer_id' => $merchants_info['merchants_id'],
                'access_id' => $param->getMid(),
                'num' => $specialInfo['all_count'],
                'serial_number' => $serial_number,
                'revoke_number' => $revoke_number,
                'before_change' => $merchants_info['remaining'],
                'after_change' => $merchants_info['remaining'] + $specialInfo['all_count'],
                'source' => 2,
                'type' => 4,
                'create_time' => date('Y-m-d H:i:s',),
                'update_time' => date('Y-m-d H:i:s',),
            ];

            // 异步写入藏品铸造数量消耗日志
            dispatch(new MerchansCastingJob($log, $param->getSpecialId()));
        }

        return $saveFlag;
    }

    /**
     * 查询藏品可用赋能,自动领取,异步
     * @param int $special_id
     * @param int $member_id
     * @return void
     * Author:xsc
     * Time:2023/10/25 13:26
     */
    private function dealPrivilegeAutoReceive(int $special_id, int $goods_id)
    {
        // 查询藏品可用赋能
        $privilege_list = SpecialPrivilegeRepo::getInstance()->getList(['special_id' => $special_id, 'status' => 1]);
        if (!$privilege_list) {
            return;
        }
        foreach ($privilege_list as $value) {
            dispatch(new PrivilegeAutoReceiveJob($value['id'], $goods_id));
        }
    }

    /**
     * 从藏品的缓存中随机获取一个商品id
     * @param int $special_id
     * @return bool|mixed|\Redis
     * @throws \RedisException
     * Author:xsc
     * Time:2023/9/6 19:17
     */
    private function getGoodsId(int $special_id)
    {
        $redis = RedisService::getInstance();
        $key = Constant::GOODS_LIST . $special_id;

        $result = false;
        while ($result == false) {
            $count = $redis->llen($key);
            if ($count <= 0) {
                Log::info('获取goods_id无库存:' . $special_id);
                return false;
            }

            $goods_id = $redis->lindex($key, mt_rand(0, $count - 1));
            if (!$goods_id) {
                Log::info('获取goods_id失败:' . $special_id);
                return false;
            }
            $result = $redis->lRem($key, $goods_id);
        }
        return $goods_id;
    }

    /**
     * goods_id放回到redis队列最后
     * @param int $special_id
     * @param int $goods_id
     * @return void
     * @throws \RedisException
     * Author:xsc
     * Time:2023/9/6 20:40
     */
    private function pushGoodsId(int $special_id, int $goods_id)
    {
        $redis = RedisService::getInstance();
        $key = Constant::GOODS_LIST . $special_id;
        $redis->rPush($key, $goods_id);
    }

}
